home *** CD-ROM | disk | FTP | other *** search
Wrap
/* File: StorageClassUTFunctions.c Contains: All device specific functions Version: 1.0 Copyright: © 1998 by Apple Computer, Inc., all rights reserved. */ #include <Disks.h> #include <DriverGestalt.h> #include <Gestalt.h> #include <NameRegistry.h> #include <Power.h> #include <Resources.h> #include <Strings.h> #include "StorageClassUTFunctions.h" #include "StorageClassUTDriver.h" #include "SampleStorageDriverAPI.h" #include "BlockDriverPriv.h" #include "SampleStorageVersion.h" #include "DriverIcons.h" // These are making an early appearance here in the Driver. We will add the support for these // now, and once Allegro ships, we will be ready. // These should be removed once the Universal Headers 3.2 are released. enum { kdgPhysDriveIconSuite = FOUR_CHAR_CODE('dics'), /* Return a pointer to a IconFamily ('icns') data structure for */ /* Disk Driver physical drive (formerly in csCode 22) in driverGestaltResponse. */ kdgMediaIconSuite = FOUR_CHAR_CODE('mics'), /* Return a pointer to a IconFamily ('icns') data structure for */ /* Disk Driver media (formerly in csCode 21) in driverGestaltResponse. */ /* See IconServices.r for information detailing the 'icns' resource data format */ kdgMediaName = FOUR_CHAR_CODE('mnam') /* Return a pointer to a pascal string describing the Disk Driver (formerly in csCode 21) in driverGestaltResponse. */ }; enum { kReadWriteRetryCount = 1, kFormatRequestSenseRetryCount = 5 }; typedef struct OurSleepQRec { SleepQRec theSleepQRec; Boolean isInSleep; } OurSleepQRec, *OurSleepQRecPtr; typedef struct DriveRequestPB { StorageExecuteCommandPB executePB; ParmBlkPtr theIOPB; IOCommandID ioCommandID; IOCommandKind ioCommandKind; OSStatus status; UInt16 retryCount; Boolean doWrite; } DriveRequestPB, *DriveRequestPBPtr; typedef struct OurParamBlock { DriveRequestPB drivePB; // Global Control/Status fields for the driver Boolean isFloppy; Boolean isWriteProtected; Boolean doInternalReadWrite; Boolean diskInDrive; UInt8 currentExecutionState; // This is to workaround a bug in the PowerPC native version of the AddDrive // call in systems before 8.5, where one needs to be added to the desired // drive number before calling AddDrive. Boolean addOneToAddDrive; SInt16 drvrRefNum; // Our driver reference number ReadCapacityData getCapacity; UInt8 sense[40]; DriveRec theDrive; VolumeRec theVolume; OurSleepQRec theSleepQRec; Str32 DriveInfoString; } OurParamBlock; // The values for the states of the state machines enum { // Mount State Machine kMountStartState = 1, kMountTURDoneState, kMountRequestSenseDoneState, kMountGetGeometryDoneState, kMountCheckGetGeometryErrorDone, kMountReadPossibleCapacitiesDone, kMountCheckWriteProtectDoneState, kMountPreventRemovalDoneState, // Eject State Machine kEjectStartState, kEjectAllowRemovalDone, kEjectCartridgeDone, // Format State Machine kFormatStartState, kFormatDoneState, kFormatWaitDoneState, kFormatRequestSenseDoneState, kFormatGetGeometryDoneState, kFormatCheckWriteProtectDoneState, kFormatPreventRemovalDoneState, // Check for disk reinsertion kReinsertionCheckStart, kReinsertionTURDoneState, kReinsertionRequestSenseDone }; /* Driver Control Codes */ enum { kcsSetBootPartitionCode = 44, // Set partition startup control <6/29/94> kcsSetMountPartitionCode = 45, // Set partition mount control <6/29/94> kcsSetWriteProtectCode = 46, // Set write protect control <6/29/94> kcsClearMountPartitionCode = 48, // Clear partition mount control <6/29/94> kcsClearWriteProtectCode = 49, // Clear write protect control <6/29/94> kcsMountVolume = 60 // Mount volume control }; /* Driver Status Codes */ enum { kcsGetBootPartitionStatus = 44, // Startup CDev support <7/20/94> kcsGetMountPartitionStatus = 45, // Drive Setup support <7/20/94> kcsGetWriteProtect = 46, // Drive Setup support <7/20/94> GetDrvCapacity = 125, // get drive capacity LastErrCode = 127, // = 7F hex kcsGetMediaCapacity = 128 // get media info (to support HFS+) }; // Apple driver specific #define physIoCode 17 // Local Functions Prototypes void InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec); void RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec); long SleepNotification(long message, SleepQRecPtr sleepRec); // These supporting functions are in this file… OSStatus MountSecondaryInterrupt( void *p1, void *p2); void MountTheCartridge( void *theCurrentPB ); void EjectTheCartridge( void *theCurrentPB ); OSStatus FormatCompletionInterrupt( void *p1, void *p2); void FormatTheCartridge( void *theCurrentPB ); // ATAPI/SCSI-2 Device Commands OSStatus CheckWriteProtect(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion); OSStatus TUR(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion); OSStatus GetMediaGeometry(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion); OSStatus ReadFormatCapacity(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion); OSStatus RequestSense(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion); OSStatus FormatFloppyCartridge(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion); OSStatus EjectCartridge(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion); OSStatus PreventAllowRemoval(OurParamBlock *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion); OSStatus ReinsertionSecondaryInterrupt( void *p1, void *p2); void CheckForCartridgeReinsertion( void *theCurrentPB ); OSStatus DoReadWriteCommand( OurParamBlock *theDriverPB, Boolean doWrite); OSStatus ReadWriteBlock( OurParamBlock *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync); void ReadWriteCompletion( void *theDriverPB ); void WriteRequestSenseCompletion( void *theDriverPB ); void RequestSenseOnErrorCompletion( void *theDriverPB ); void CheckUnexpectedRemoval(DriveRecPtr drive); void RemoveVolume(DriveRecPtr drvRec, UInt16 volRef); Boolean SetPowerMode(DriveRecPtr drive, UInt16 powerMode, UInt16 timerSecs); VolumeRecPtr GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum); VCB* MountedVolOfDrive(DriveRecPtr drive); SInt16 NextQDrive(); void FlushDriveWriteCache( void ); VolumeRecPtr CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset); void RemoveDrive(DriveRecPtr theDrivePtr); void UpdateQ(SInt16 qDrive, SInt32 newSize); SInt32 NextPartitionID(DriveRecPtr drive); void SpinDownDrive(DriveRecPtr drive); Boolean InstallDrive(DriveRecPtr drive, Boolean mountVols); OSErr MountUnmountVolumes( DriveRecPtr drive ); //---------------------------------------------------------------------------------- // Globals //---------------------------------------------------------------------------------- static DriverLocationIcon gDriveIcon; // static structure for control calls static StorageClassDispatchTablePtr gItsTheDispatchTable = nil; // The class's dispatch table static TimerID gInterruptTimer = 0; static OurParamBlock gTheParamBlock; //---------------------------------------------------------------------------------- // Driver calls //---------------------------------------------------------------------------------- // Always run at task level, can allocate and move memory. OSStatus DriverInitializeCmd ( AddressSpaceID addressSpaceID, DriverInitInfoPtr initialInfo) { #pragma unused (addressSpaceID, initialInfo) OSStatus err = noErr; UInt32 gestaltResponse; IfDebugging("\pInitialize Driver"); gTheParamBlock.drvrRefNum = initialInfo->refNum; gTheParamBlock.drivePB.theIOPB = nil; gTheParamBlock.currentExecutionState = kMountStartState; gTheParamBlock.doInternalReadWrite = false; gTheParamBlock.diskInDrive = false; gTheParamBlock.isFloppy = false; // Check System version to see if we need to add one to AddDrive calls Gestalt (gestaltSystemVersion,(long *) &gestaltResponse); if( (gestaltResponse&0xFFFF) >= 0x0850 ) { // We are on system 8.5 or later, we do not need to add 1 to AddDrive calls gTheParamBlock.addOneToAddDrive = false; // Set up our color icon family BuildMediaIconFamily(); } else { gTheParamBlock.addOneToAddDrive = true; } // Clear out the Drive Record BlockZero((Ptr) &gTheParamBlock.theDrive, sizeof(DriveRec)); // Clear out the Volume Record BlockZero((Ptr) &gTheParamBlock.theVolume, sizeof(VolumeRec)); // Build the Drive Info string returned in Status/Control calls { Str32 tempStr; PStrCopy( gTheParamBlock.DriveInfoString, "\pUSB (v"); CStrToPStr(tempStr, kStorageStringVersShort); // driver version PStrCat(gTheParamBlock.DriveInfoString, tempStr); // driver version PStrCat(gTheParamBlock.DriveInfoString, "\p)"); } BlockZero((Ptr) &gTheParamBlock.theSleepQRec, sizeof(OurSleepQRec)); InstallInSleepQueue( &gTheParamBlock.theSleepQRec ); return (err); } // Always run at task level, can allocate and move memory. OSStatus DriverFinalizeCmd (DriverFinalInfoPtr finalInfo) { #pragma unused (finalInfo) OSStatus err = noErr; DestroyMediaIconFamily(); RemoveFromSleepQueue( &gTheParamBlock.theSleepQRec ); //DriverCloseCmd (nil); return (err); } // Always run at task level, can allocate and move memory. OSStatus DriverSupersededCmd (DriverSupersededInfoPtr supersededInfo) { #pragma unused (supersededInfo) OSErr err = noErr; return (err); } // Always run at task level, can allocate and move memory. OSStatus DriverReplaceCmd ( AddressSpaceID addressSpaceID, DriverReplaceInfoPtr replaceInfo) { #pragma unused (addressSpaceID, replaceInfo) OSStatus err = noErr; return (err); } // Always run at task level, can allocate and move memory. OSStatus DriverOpenCmd ( AddressSpaceID addressSpaceID, ParmBlkPtr pb) { #pragma unused (addressSpaceID, pb) OSStatus err = noErr; // Let the world know we can do DriverGestalt calls DriverGestaltOn(gTheParamBlock.drvrRefNum); return(err); } // Always run at task level, can allocate and move memory. OSStatus DriverCloseCmd (ParmBlkPtr pb) { #pragma unused (pb) OSStatus err = noErr; gTheParamBlock.diskInDrive = false; // If an interrupt timer is set, cancel it! if( gInterruptTimer != 0 ) { AbsoluteTime timeLeft; // Cancel any pending timers CancelTimer( gInterruptTimer, &timeLeft); gInterruptTimer = 0; } // Force volume off line gTheParamBlock.theDrive.busState = kOFFLINE; CheckUnexpectedRemoval(&gTheParamBlock.theDrive); // If a command is pending, we should cancel it. // Since we currently have no way of informing the Class driver to abort, // we will wait for the class driver to tell us the command has finished. while( gTheParamBlock.drivePB.theIOPB != nil ); // Dequeue all drive volumes RemoveDrive(&gTheParamBlock.theDrive); (void) ((gItsTheDispatchTable)->pStorageControl)(kControlEnableRemoval, nil); return (err); } // May run at interrupt level, CANNOT allocate or move memory. OSStatus DriverControlCmd ( AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, ParmBlkPtr pb) { #pragma unused ( addressSpaceID ) OSStatus err = noErr; CntrlParamPtr pbPtr; UInt32 *altParams; // local pointer to alternate control parameters DriveRecPtr drive = &gTheParamBlock.theDrive; VolumeRecPtr vol = nil; // pointer to our volume record structure BlockZero((Ptr) &gTheParamBlock.drivePB, sizeof(DriveRequestPB)); gTheParamBlock.drivePB.theIOPB = nil; gTheParamBlock.drivePB.ioCommandID = ioCommandID; gTheParamBlock.drivePB.ioCommandKind = ioCommandKind; gTheParamBlock.drivePB.retryCount = 0; gTheParamBlock.drivePB.doWrite = false; pbPtr = (CntrlParamPtr) pb; altParams = (UInt32 *)&pbPtr->csParam[0]; // alternate parameters // Parse the control codes… //SysDebugStr("\pControl Call"); switch(pbPtr->csCode) { case killCode: { // This driver does not support the killCode, // return back -1 as per TechNote DV 17: Sony Driver err = -1; } break; case kVerify: // Verify the media, this should only be called for floppies { if(gTheParamBlock.theDrive.capacity == 0) { err = nsDrvErr; // no Media is inserted, return an error } else if( gTheParamBlock.isFloppy == true ) { UInt16 diskCapacity; UInt16 startBlock; UInt16 numberBlocks = 16; UInt8 *blockBuffer; diskCapacity = gTheParamBlock.theDrive.capacity; blockBuffer = (UInt8 *) NewPtrSysClear(numberBlocks * gTheParamBlock.theDrive.blockSize); if(blockBuffer == nil) { err = verErr; } else { for (startBlock = 0; startBlock<diskCapacity; startBlock+=numberBlocks) { gTheParamBlock.doInternalReadWrite = true; err = ReadWriteBlock( &gTheParamBlock, startBlock, numberBlocks,(Ptr) blockBuffer, false, false); gTheParamBlock.doInternalReadWrite = false; // an Error has occured if ( err != noErr ) { // report a verify error err = verErr; break; } } DisposePtr((Ptr) blockBuffer); } } else { err = noErr; // For the regular cartridge, just report noErr } } break; case kFormat: { if (gTheParamBlock.isWriteProtected == true) { // check for write protect err = wPrErr; break; } if(gTheParamBlock.theDrive.capacity == 0) { err = nsDrvErr; // no Media is inserted, return an error } else if( gTheParamBlock.isFloppy == true ) { gTheParamBlock.currentExecutionState = kFormatStartState; FormatTheCartridge( &gTheParamBlock ); err = gTheParamBlock.drivePB.status; } else { err = noErr; // For the regular cartridge, just report noErr } } break; case kEject: { // We get this call whenever a volume is put away (dragged to trash). // We don't dequeue the DrvQEl or delete the drive record in case the // system may remount it later (via kcsMountVolume control call). // However, if the drive is ejectable (PCMCIA) we issue the eject of // the drive if there are no mounted (online) volumes for it. Boolean hasMountedVolume = (MountedVolOfDrive(drive) != nil); if (!hasMountedVolume) // If no more mounted volumes… { //SpinDownDrive(drive); // spin down drive to conserve power gTheParamBlock.currentExecutionState = kEjectStartState; EjectTheCartridge( &gTheParamBlock ); } } break; case kSetTagBuffer: // This is a floppy specific control call { err = controlErr; // Return a controlErr, since we do not support this call } break; case kTrackCache: // This is a floppy specific control call { err = noErr; // The driver does not keep an internal write cache, // terfore, it will act like it does and return noErr } break; case physIoCode: // 1 = use physical offset, else use partition offset { vol = GetVolume(drive, pbPtr->ioVRefNum, 0); if ( vol ) // If volume requested exists { drive = (DriveRecPtr)vol->drivePtr; // get its drive record } else // else if drive was requested and exists { if ( drive ) { vol = drive->nextVol; // use first volume on drive } } if (!drive || !vol) // If we did not find a drive and volume { return(nsDrvErr); // report drive not found error } vol->curoffset = (*(UInt16 *)pbPtr->csParam == 1) ? 0 : vol->partoffset; } break; case kDriveIcon: // Return icon displayed during media initialization case kMediaIcon: // Return icon displayed on desktop for media { vol = GetVolume(drive, pbPtr->ioVRefNum, 0); if ( !vol ) // If we did not find a drive and volume { err = nsDrvErr; // report drive not found error break; } BlockMove(vol->mediaIconPtr, &gDriveIcon.ataLocationIcon, sizeof(DiskIcon)); // Copy over the DriveInfo string from the global param block PStrCopy(gDriveIcon.ataLocationString, gTheParamBlock.DriveInfoString); // Finally, return the pointer to the icon in csParam *(DriverLocationIcon**)pbPtr->csParam = &gDriveIcon; err = noErr; // clear any error from above } break; case kDriveInfo: // DRIVE INFO request (was from ATA maanger) { pbPtr->csParam[0] = 0; // Upper Word is always 0; pbPtr->csParam[1] = 0; // Clear Lower Word if( gTheParamBlock.isFloppy == true ) { // If we currently have a floppy loaded, return the following info pbPtr->csParam[1] = ( 1 << 11 ) // Drive cardinality (0 - primary, 1 - secondary) | ( 0 << 10 ) // Media removability ( 0 - removable, 1 - fixed ) | ( 0 << 9 ) // Interface ( 0 - floppy, 1 - SCSI ) | ( 1 << 8 ) // Location ( 0 - internal, 1 - external ) // Bits 4,5,6,7 are reserved | 4; // Drive Type ( use 4 for SuperDrive for compatibility ) } else if(gTheParamBlock.theDrive.blockSize != 0) { // If we currently have a cartridge loaded, return the following info pbPtr->csParam[1] = ( 1 << 11 ) // Drive cardinality (0 - primary, 1 - secondary) | ( 0 << 10 ) // Media removability ( 0 - removable, 1 - fixed ) | ( 1 << 9 ) // Interface ( 0 - floppy, 1 - SCSI ) | ( 1 << 8 ) // Location ( 0 - internal, 1 - external ) // Bits 4,5,6,7 are reserved | 1; // Drive Type ( use 1 for Unknown drive (Not a floppy) ) } } break; case kDriverConfigureCode: { DriverConfigParam *configPtr; // local pointer to drive config structure configPtr = (DriverConfigParam *) pbPtr; switch(configPtr->driverConfigureSelector) { case kdgFlush: { // Issue a Flush cache Command to the drive FlushDriveWriteCache(); } break; default: { err = controlErr; } break; } } break; case kcsSetBootPartitionCode: // Set Startup Partition case kcsSetMountPartitionCode: // Set Mount Partition case kcsClearMountPartitionCode: // Clear Mount Partition case kcsSetWriteProtectCode: // Set Write Protect on Partition case kcsClearWriteProtectCode: // Clear Write Protect on Partition { err = controlErr; } break; case kRegisterPartition: // Register New Partition { DrvQElPtr theDrvQEl; // drive queue element pointer // PCX will call this function when it wants to redefine a partition. It will // pass in the drive queue element pointer of the partition to redefine, the // new starting physical block offset, and the new block length. vol = nil; err = nsDrvErr; // assume an invalid volume theDrvQEl = (DrvQElPtr) altParams[THE_DRIVE]; if ( theDrvQEl ) // if valid queue element pointer { vol = GetVolume(drive, theDrvQEl->dQDrive, 0); if ( vol ) // and valid volume reference { vol->partoffset = altParams[THE_PHYS_START]; // new partition offset vol->curoffset = vol->partoffset; // current offset changes also vol->partblks = altParams[THE_PHYS_SIZE]; // new partition size UpdateQ(theDrvQEl->dQDrive, vol->partblks); // Update drive queue capacity vol->partmounted = true; // volume will be mounted by PCX err = noErr; // clear error } } } break; case kGetADrive: // Get A Drive (Create New Partition) { // PCX calls this function to add a new partition. The new partition's DrvQElPtr is // returned. NOTE: If the driver handles multiple drives note that PCX does not pass // in the physical drive number on which to create the new partition. However, the // DrvQElPtr stored at the pointer passed in is for another partition on the drive. if (!altParams[THE_VAR_QUEL]) // verify a valid queue element handle err = paramErr; else { // create a new volume record and DrvQEl associated with the physical drive. // The new volume starts at offset 0 and has no capacity yet. By default, the // partition will not have a partition map entry on the media. vol = CreateVolume(drive, NextPartitionID(drive), 0, 0 ); if ( vol ) { UInt16 volNumber; vol->vRefNum = NextQDrive(); // assign a logical drive number volNumber = vol->vRefNum; // Check to see if we need to add one to the AddDrive call if( gTheParamBlock.addOneToAddDrive == true) { volNumber += 1; } AddDrive(gTheParamBlock.drvrRefNum, volNumber,(DrvQElPtr) &vol->driveStatus.qLink); // Return the DrvQElPtr at the location passed in… *((DrvQElPtr*)altParams[THE_VAR_QUEL]) = (DrvQElPtr) &vol->driveStatus.qLink; } else err = controlErr; } } break; case kcsMountVolume: // Mount Volume { err = controlErr; } break; case kMediaPowerCSCode: // Set Power Mode { unsigned short powerMode = (UInt16) *((SInt8*)&(pbPtr->csParam[0])); if (powerMode > kMediaModeOff) // Verify a valid request { err = paramErr; break; } // Translate kMediaModeSuspend and kMediaModeOff to our spindown mode, if ((powerMode == kMediaModeOff) || (powerMode == kMediaModeSuspend)) { SpinDownDrive(drive); } else if (drive->inSleepMode) { // else all others (kMediaModeOn and kMediaModeStandBy) translate to our active mode. // If drive is in sleepMode, then wake it up, else assume active. } } break; case 500: { AbsoluteTime theWait; IfDebugging("\pSet Dispatch Table"); gItsTheDispatchTable = ( StorageClassDispatchTablePtr ) *((UInt32 *)(&pbPtr->csParam[0])); // The class's dispatch table (void) ((gItsTheDispatchTable)->pStorageControl)(kControlDisableRemoval, nil); theWait = DurationToAbsolute(durationSecond); theWait = AddAbsoluteToAbsolute(UpTime(), theWait); err = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer); } break; case 21315: // Disk copy format and copy { UInt16 theFormat; UInt8 *theDiskImage; //SysDebugStr("\pDiskCopy"); if( gTheParamBlock.isWriteProtected == true) { err = wPrErr; // disk is write protected break; } theFormat = pbPtr->csParam[0]; theDiskImage = (UInt8 *) *((UInt32 *) &pbPtr->csParam[1]); // Extract the pointer to the data gTheParamBlock.doInternalReadWrite = true; err = ReadWriteBlock( &gTheParamBlock, 0, gTheParamBlock.theDrive.capacity,(Ptr) theDiskImage, true, false ); gTheParamBlock.doInternalReadWrite = false; // an Error has occured if ( err != noErr ) { // report an error err = paramErr; } } break; default: { err = controlErr; } break; } //SysDebugStr("\pEnd Control Call"); return(err); } // May run at interrupt level, CANNOT allocate or move memory. OSStatus DriverStatusCmd ( AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, ParmBlkPtr pb) { #pragma unused (addressSpaceID, ioCommandID, ioCommandKind) OSStatus err = noErr; CntrlParamPtr pbPtr; UInt32 *altParams; // alternate csParams as long words VolumeRecPtr vol = nil; DriveRecPtr drive = &gTheParamBlock.theDrive; BlockZero((Ptr) &gTheParamBlock.drivePB, sizeof(DriveRequestPB)); gTheParamBlock.drivePB.theIOPB = nil; gTheParamBlock.drivePB.ioCommandID = ioCommandID; gTheParamBlock.drivePB.ioCommandKind = ioCommandKind; gTheParamBlock.drivePB.retryCount = 0; gTheParamBlock.drivePB.doWrite = false; pbPtr = (CntrlParamPtr) pb; altParams = (UInt32 *) &pbPtr->csParam[0]; // alternate parameters // Get the drive and volume record of the logical drive specified. Some status calls // use ioVRefNum differently so verify a valid reference based upon the call. We // do the volume and drive validation here to avoid doing so for every function below. //SysDebugStr("\pStatus Call"); switch(pbPtr->csCode) { // These calls can optionally set ioVRefNum = 0 and use the partition ID in csParam[0] // altParams[0] = partition block address; case kcsGetBootPartitionStatus: case kcsGetMountPartitionStatus: case kcsGetWriteProtect: case kcsGetMediaCapacity: // for HFS+ { vol = GetVolume(drive, pbPtr->ioVRefNum, altParams[0]); if (!drive || !vol) // If we did not find a drive and volume return(nsDrvErr); // report drive not found error } break; // Otherwise, assume ioVRefNum is valid as defined. default: { vol = GetVolume(drive, pbPtr->ioVRefNum, 0); } break; } switch(pbPtr->csCode) // Finish processing the status call… { case kReturnFormatList: { if( gTheParamBlock.isFloppy == false ) { err = statusErr; } else { typedef struct FormatList { UInt32 NumberBlock; UInt8 TSSValid : 1; UInt8 IsCurrentFormat : 1; UInt8 CanFormat : 1; UInt8 Density : 1; UInt8 NoSides : 4; UInt8 SecPerTrack; UInt16 NumberTracks; } FormatList; UInt8 returnNumber; UInt8 totalFormats = 2; FormatList theFormats[2]; // Setup the Formats // Double Density Floppy Disk theFormats[0].NumberBlock = 1440; theFormats[0].TSSValid = 1; theFormats[0].Density = 0; // 0 means single density, 1 means Double Density theFormats[0].NoSides = 2; theFormats[0].SecPerTrack = 9; theFormats[0].NumberTracks = 80; // High Density Floppy Disk theFormats[1].NumberBlock = 2880; theFormats[1].TSSValid = 1; theFormats[1].Density = 1; theFormats[1].NoSides = 2; theFormats[1].SecPerTrack = 18; theFormats[1].NumberTracks = 80; if( drive->capacity < 0x600 ) { theFormats[0].CanFormat = 0; // 0 means we can format this disk as 720K theFormats[1].CanFormat = 1; // 1 means we can not format this disk as 1.44M theFormats[0].IsCurrentFormat = 1; // 1 means 720K MFM Disk is installed theFormats[1].IsCurrentFormat = 0; // 0 means 1.44M MFM Disk is not installed } else { theFormats[0].CanFormat = 1; // 1 means we can not format this disk as 720K theFormats[1].CanFormat = 0; // 0 means we can format this disk as 1.44M theFormats[0].IsCurrentFormat = 0; // 0 means 720K MFM Disk is not installed theFormats[1].IsCurrentFormat = 1; // 1 means 1.44M MFM Disk is installed } returnNumber = *((UInt16 *) &pbPtr->csParam[0]); if( totalFormats < returnNumber ) { returnNumber = totalFormats; } BlockCopy( (Ptr) &theFormats[0], (Ptr) (*((UInt32 *) &pbPtr->csParam[1])), sizeof(FormatList) * returnNumber); *((UInt16 *) &pbPtr->csParam[0]) = returnNumber; } } break; case kDriveStatus: { BlockMove(&vol->driveStatus, (UInt8 *) &pbPtr->csParam[0], sizeof(DrvSts)); } break; case kMFMStatus: { if( gTheParamBlock.isFloppy == false ) { err = statusErr; } else { pbPtr->csParam[0] = -3; // PC Industry standard MFM (no GCR support) pbPtr->csParam[1] = -1; // MFM Disk installed if( drive->capacity < 0x600 ) { pbPtr->csParam[2] = 0; // 720K MFM Disk installed } else { pbPtr->csParam[2] = -1; // 1.44M MFM Disk installed } pbPtr->csParam[3] = -5; // Generic PC Floppy Disk Controller } } break; case kDriverGestaltCode: { DriverGestaltParam *gestaltPtr; // local pointer to drive gestalt structure vol = GetVolume(drive, pbPtr->ioVRefNum, 0); if (vol) drive = (DriveRecPtr) vol->drivePtr; // get its drive record gestaltPtr = (DriverGestaltParam *) pbPtr; switch(gestaltPtr->driverGestaltSelector) { case kdgVersion: { // Return information on the driver version NumVersion* numVersion; numVersion = GetDriverGestaltVersionResponse(gestaltPtr); numVersion->majorRev = kStorageHexMajorVers; numVersion->minorAndBugRev = kStorageHexMinorVers; numVersion->stage = kStorageReleaseStage; numVersion->nonRelRev = kStorageCurrentRelease; } break; case kdgDeviceType: { // Return the type of device--either floppy disk or removable disk DriverGestaltDevTResponse* deviceTypeResponse = GetDriverGestaltDevTResponse(gestaltPtr); if( gTheParamBlock.isFloppy == true ) { deviceTypeResponse->deviceType = kdgFloppyType; } else { deviceTypeResponse->deviceType = kdgRemovableType; } } break; case kdgInterface: { // Return the interface of the drive in ioVRefNum. DriverGestaltIntfResponse* interfaceResponse = GetDriverGestaltIntfResponse(gestaltPtr); interfaceResponse->interfaceType = 'USB '; } break; case kdgSync: { // Return true if the driver supports only synchronous behavior DriverGestaltSyncResponse* syncResponse = GetDriverGestaltSyncResponse(gestaltPtr); syncResponse->behavesSynchronously = false; } break; case kdgBoot: { // Return the ID of the boot device for PRAM storage if (!drive || !vol) err = nsDrvErr; else { //DriverGestaltBootResponse* bootResponse = GetDriverGestaltBootResponse(gestaltPtr); //bootResponse->extDev = 0; //bootResponse->partition = vol->partitionNo; //bootResponse->SIMSlot = 0; //bootResponse->SIMsRSRC = 0; err = statusErr; } } break; case kdgWide: { // Return whether driver supports large volume addressing (> 4GByte) // Unless our cartridges are greater than 4 GB, we don't support // or need to support wide block addressing Boolean* response = GetDriverGestaltBooleanResponse(gestaltPtr); *response = false; } break; case kdgPurge: // Return if we can be closed and purged from memory. { DriverGestaltPurgeResponse* response = GetDriverGestaltPurgeResponse(gestaltPtr); // Currently the driver cannot be closed or purged once installed response->purgePermission = kmNoCloseNoPurge; response->purgeDriverPointer = (Ptr) nil; } break; case kdgSupportsSwitching: { // Return whether driver supports low power control call (csCode = 70h) *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false; } break; case kdgSupportsPowerCtl: { // Return whether driver supports low power control call (csCode = 70h) *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false; } break; case kdgAPI: { // Return whether driver supports PC-Exchange Control and Status calls // related to partitioning. DriverGestaltAPIResponse* apiResponse = GetDriverGestaltAPIResponse(gestaltPtr); apiResponse->partitionCmds = true; } break; case kdgFlush: { // Return whether driver supports the Cache flush Control call, // and whether the finder should tell us to flush our cache DriverGestaltFlushResponse* response = GetDriverGestaltFlushResponse(gestaltPtr); response->canFlush = true; if(gTheParamBlock.theDrive.blockSize == 0) { response->needsFlush = false; // Don't need a flush call because there is no media } else { response->needsFlush = true; // Needs a flush call because media is present } response->canFlush = false; response->needsFlush = false; // Needs a flush call because media is present } break; case kdgEject: { // Return whether driver wants eject call for shutdown and restart // Eject on restart or shutdown DriverGestaltEjectResponse* response = GetDriverGestaltEjectResponse(gestaltPtr); response->ejectFeatures = 0L; // Clear both Dont Eject Bits, kShutDownDontEject and kRestartDontEject. } break; case kdgVMOptions: { // Return whether drive can be used for Virtual Memory // Don't support use of media for VM DriverGestaltVMOptionsResponse* response = GetDriverGestaltVMOptionsResponse(gestaltPtr); response->vmOptions = kAllowVMNoneMask; } break; case kdgMediaInfo: { // Return back specific information about our media DriverGestaltMediaInfoResponse* response = GetDriverGestaltMediaInfoResponse(gestaltPtr); //response->numberBlocks = vol->partblks; response->numberBlocks = gTheParamBlock.theDrive.capacity; response->blockSize = gTheParamBlock.theDrive.blockSize; if(gTheParamBlock.theDrive.blockSize == 0) { response->mediaType = kMediaTypeNoMedia; } else { response->mediaType = kMediaTypeUnknown; } } break; /* Return a pointer to a IconFamily ('icns') data structure for */ /* Disk Driver physical drive (formerly in csCode 22) in driverGestaltResponse. */ case kdgPhysDriveIconSuite: /* Return a pointer to a IconFamily ('icns') data structure for */ /* Disk Driver media (formerly in csCode 21) in driverGestaltResponse. */ case kdgMediaIconSuite: { // If the media is currently a floppy, return a statusErr so the system will // handle the 3D Color icon information if( gTheParamBlock.isFloppy == true ) { if(FloppyMediaIconFamily == nil ) { err = statusErr; } else { gestaltPtr->driverGestaltResponse = (UInt32) *FloppyMediaIconFamily; } } else { if(CartridgeMediaIconFamily == nil ) { err = statusErr; } else { gestaltPtr->driverGestaltResponse = (UInt32) CartridgeMediaIconFamily; } } } break; case kdgMediaName: { /* Return a pointer to a pascal string describing the Disk Driver (formerly in csCode 21) in driverGestaltResponse. */ gestaltPtr->driverGestaltResponse = (UInt32) &gTheParamBlock.DriveInfoString; } break; default: err = statusErr; // unknown DriverGestalt selector } } break; case kcsGetBootPartitionStatus: // Is this the boot partition? { // Since USB drives don't support booting, always report false pbPtr->csParam[0] = 0; err = statusErr; } break; case kcsGetMountPartitionStatus: // Is this an automount partition? { pbPtr->csParam[0] = 1; } break; case kcsGetWriteProtect: // Is this partition write protected? { pbPtr->csParam[0] = (vol->driveStatus.writeProt) ? 1 : 0; } break; case kGetPartInfo: { // PCX will call this function to get info on the specified partition. // Return the physical drive reference, the starting block offset, and // the partition ID (any relative non-zero reference). partInfoRecPtr thePartInfo = (partInfoRecPtr)altParams[kPartInfoResponse]; // SCSIID is not defined for non-SCSI devices. For now we use the LUN only *((UInt32 *)&thePartInfo->SCSIID) = 0; thePartInfo->physPartitionLoc = vol->partoffset; thePartInfo->partitionNumber = vol->partitionNo; } break; case kMediaPowerCSCode: { // Return the current power mode of the drive. NOTE: The power modes (kMediaModeOn, // kMediaModeStandBy, kMediaModeSuspend, and kMediaModeOff) are not the same as the ATA modes. // The primary difference is kMediaModeSuspend = Standby and kMediaModeStandBy = Idle UInt8 powerMode = kMediaModeOn; // Assume drive is active if (drive->inSleepMode) // check if in sleep mode powerMode = kMediaModeOff; // Return the power mode in the upper byte of csParam[0], lower byte is cleared pbPtr->csParam[0] = powerMode << 8; } break; case GetDrvCapacity: // Return physical drive capacity { pbPtr->csParam[0] = (SInt16)drive->capacity; // lower word of capacity pbPtr->csParam[1] = (SInt16)(drive->capacity >> 16); // upper word of capacity } break; case kcsGetMediaCapacity: // return media info for HFS+ { //GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->numberBlocks = vol->partblks; GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->numberBlocks = gTheParamBlock.theDrive.capacity; GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->blockSize = gTheParamBlock.theDrive.blockSize; GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->mediaType = kMediaTypeNoMedia; if(gTheParamBlock.theDrive.blockSize == 0) { GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->mediaType = kMediaTypeNoMedia; } else { GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->mediaType = kMediaTypeUnknown; } } break; case 17494: // DiskCopy version supported { pbPtr->csParam[0] = 0x0410; // We support the Diskcopy 4.1 API } break; default: // Unrecognized status call { err = statusErr; } break; } //SysDebugStr("\pEnd Status Call"); return(err); } // May run at interrupt level, CANNOT allocate or move memory. OSStatus DriverReadCmd ( AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, ParmBlkPtr pb) { #pragma unused ( addressSpaceID ) OSStatus err = ioErr; if( gTheParamBlock.diskInDrive == false) { err = ioErr; } else { //SysDebugStr("\pRead Call;g"); // Clear out the Drive request PB BlockZero((Ptr) &gTheParamBlock.drivePB, sizeof(DriveRequestPB)); gTheParamBlock.drivePB.theIOPB = pb; gTheParamBlock.drivePB.ioCommandID = ioCommandID; gTheParamBlock.drivePB.ioCommandKind = ioCommandKind; gTheParamBlock.drivePB.retryCount = 0; gTheParamBlock.drivePB.doWrite = false; err = DoReadWriteCommand( &gTheParamBlock, false); } return(err); } // May run at interrupt level, CANNOT allocate or move memory. OSStatus DriverWriteCmd ( AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, ParmBlkPtr pb) { #pragma unused ( addressSpaceID ) OSStatus err = ioErr; if( gTheParamBlock.diskInDrive == false ) { err = ioErr; } else { //SysDebugStr("\pWrite Call"); // Clear out the Drive request PB BlockZero((Ptr) &gTheParamBlock.drivePB, sizeof(DriveRequestPB)); gTheParamBlock.drivePB.theIOPB = pb; gTheParamBlock.drivePB.ioCommandID = ioCommandID; gTheParamBlock.drivePB.ioCommandKind = ioCommandKind; gTheParamBlock.drivePB.retryCount = 0; gTheParamBlock.drivePB.doWrite = true; err = DoReadWriteCommand( &gTheParamBlock, true); } return(err); } // May run at interrupt level, CANNOT allocate or move memory. OSStatus DriverKillIOCmd (ParmBlkPtr pb) { #pragma unused (pb) OSStatus err = noErr; return (err); } void InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec) { Boolean hasPMDispatch; SInt32 status; // To do most power management we must have the Power Manager Dispatch routines. if (Gestalt(gestaltPowerMgrAttr, &status) == 0) hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false; if (hasPMDispatch) { // Power Manager manages a spindown timer for the internal drive. When the // timer expires it calls all routines registered in the HD Queue, and then, // if PG&E is present (Powerbooks) it will turn off power to the drive. // If PG&E is not present (Desktops) Power Manager can't turn off power and // expects one of the queue routines to reduce drive power instead. // Therefore, if we manage the internal drive we should be in the HD Queue. ourSleepQRec->theSleepQRec.sleepQLink = nil; ourSleepQRec->theSleepQRec.sleepQType = sleepQType; ourSleepQRec->theSleepQRec.sleepQProc = NewSleepQProc( &SleepNotification); //ourSleepQRec->theSleepQRec.sleepQProc = (SleepQUPP) NewSleepQProc( &SleepNotification); ourSleepQRec->theSleepQRec.sleepQFlags = 0; // reserved ourSleepQRec->isInSleep = false; SleepQInstall( (SleepQRecPtr) ourSleepQRec ); } } void RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec) { Boolean hasPMDispatch; SInt32 status; // To do most power management we must have the Power Manager Dispatch routines. if (Gestalt(gestaltPowerMgrAttr, &status) == 0) hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false; if (hasPMDispatch) { SleepQRemove( (SleepQRecPtr) ourSleepQRec ); } } long SleepNotification(long message, SleepQRecPtr sleepRec) { OurSleepQRecPtr ourSleepRec = (OurSleepQRecPtr) sleepRec; SInt32 response = noErr; // assume we accept command switch(message) { case dozeRequest: // ##### Request to doze ###### case dozeDemand: // ##### Going to doze now ###### case sleepRequest: // ##### Request to sleep ###### case sleepDemand: // ##### Going to sleep now ###### case sleepNow: ourSleepRec->isInSleep = true; break; case sleepWakeUp: // ##### Wakeup from sleep ###### case sleepRevoke: // ##### Someone denied sleep ###### case dozeWakeUp: // ##### Wakeup from doze ###### ourSleepRec->isInSleep = false; break; } return(response); } OSStatus MountSecondaryInterrupt( void *p1, void *p2) { #pragma unused ( p1, p2 ) OSStatus status; AbsoluteTime theWait; UInt32 classDriverStatus; IfDebugging("\pMountSecondaryInterrupt"); gInterruptTimer = 0; if(gItsTheDispatchTable) { // Check if class driver is configured, if not setup another interrupt status = ((gItsTheDispatchTable)->pStorageStatus)(kStatusConfiguration, &classDriverStatus); if (status == noErr) { if ((classDriverStatus == kConfigureInProgress) || (gTheParamBlock.theSleepQRec.isInSleep == true)) { theWait = DurationToAbsolute(durationSecond); theWait = AddAbsoluteToAbsolute(UpTime(), theWait); status = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer); return noErr; } else if (classDriverStatus == kConfigureComplete) { gTheParamBlock.currentExecutionState = kMountStartState; MountTheCartridge( &gTheParamBlock ); } } } return noErr; } void MountTheCartridge( void *theCurrentPB ) { OSStatus err = noErr; AbsoluteTime oneSecondWait; DriveRec *drive; OurParamBlock *ourPB; ourPB = ((OurParamBlock *) theCurrentPB); drive = &ourPB->theDrive; oneSecondWait = DurationToAbsolute(durationSecond); //oneSecondWait = DurationToAbsolute(durationMillisecond * 20); switch (ourPB->currentExecutionState ) { case kMountStartState: { ourPB->currentExecutionState = kMountTURDoneState; err = TUR( ourPB, &MountTheCartridge); } break; case kMountTURDoneState: { ourPB->currentExecutionState = kMountRequestSenseDoneState; err = RequestSense( ourPB, &MountTheCartridge); } break; case kMountRequestSenseDoneState: { if((ourPB->sense[12] == 0x00) && (ourPB->drivePB.executePB.status == noErr)) { ourPB->currentExecutionState = kMountGetGeometryDoneState; ourPB->getCapacity.lastLogicalBlock = 0; ourPB->getCapacity.blockLength = 0; err = GetMediaGeometry( ourPB, &MountTheCartridge); } else { ourPB->currentExecutionState = kMountStartState; oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait); err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer); err = noErr; } } break; case kMountGetGeometryDoneState: { ReadCapacityData *getGeometry; getGeometry = (ReadCapacityData *) &ourPB->getCapacity; drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in drive->blockSize = getGeometry->blockLength; if((drive->capacity == 0) || (drive->blockSize == 0)) { //Find out why the capacity is zero ourPB->currentExecutionState = kMountCheckGetGeometryErrorDone; err = RequestSense( ourPB, &MountTheCartridge); break; } if( drive->capacity >0x0C00) { ourPB->isFloppy = false; } else { ourPB->isFloppy = true; } ourPB->diskInDrive = true; // Check to see if media is write protected ourPB->currentExecutionState = kMountCheckWriteProtectDoneState; err = CheckWriteProtect(ourPB, &MountTheCartridge); } break; case kMountCheckGetGeometryErrorDone: { if( (ourPB->sense[12] == 0x30) && (ourPB->sense[13] == 0x01)) { // We have an unformatted cartridge, find out what format it can have ourPB->currentExecutionState = kMountReadPossibleCapacitiesDone; err = ReadFormatCapacity(ourPB, &MountTheCartridge); } else { ourPB->currentExecutionState = kMountStartState; // reset the machine oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait); err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer); } } break; case kMountReadPossibleCapacitiesDone: { if( (ourPB->sense[3] >= 0x08) && (ourPB->sense[8] & 0x01 == 0x01)) { // this is the maximum format for this cartridge drive->capacity = *((UInt32 *) &ourPB->sense[4]); drive->blockSize = *((UInt16 *) &ourPB->sense[10]); if( drive->capacity >0x0C00) { ourPB->isFloppy = false; } else { ourPB->isFloppy = true; } // Check to see if media is write protected ourPB->currentExecutionState = kMountCheckWriteProtectDoneState; err = CheckWriteProtect(ourPB, &MountTheCartridge); break; } // An error occurred. It could be: // 1. we didn't get a complete descriptor // 2. the drive doesn't recognize this media type // Reset and try again. ( Should we possibly eject the media instead??) ourPB->currentExecutionState = kMountStartState; // reset the machine oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait); err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer); } break; case kMountCheckWriteProtectDoneState: { if ( ( ourPB->sense[3] & 0x80 ) != 0 ) { ourPB->isWriteProtected = true; } else { ourPB->isWriteProtected = false; } ourPB->currentExecutionState = kMountPreventRemovalDoneState; err = PreventAllowRemoval(ourPB, true, &MountTheCartridge); } break; case kMountPreventRemovalDoneState: { // The device is now mounted, and the Media is locked in place. // There is nothing left for us to do, so just break. if (InstallDrive( drive, true ) == false) { gTheParamBlock.currentExecutionState = kEjectStartState; EjectTheCartridge( &gTheParamBlock ); } } break; } } void EjectTheCartridge( void *theCurrentPB ) { OSStatus err = noErr; OurParamBlock *ourPB; ourPB = ((OurParamBlock *) theCurrentPB); switch (ourPB->currentExecutionState ) { case kEjectStartState: { ourPB->currentExecutionState = kEjectAllowRemovalDone; err = PreventAllowRemoval(ourPB, false, &EjectTheCartridge); // Allow for Ejects } break; case kEjectAllowRemovalDone: { ourPB->currentExecutionState = kEjectCartridgeDone; EjectCartridge(&gTheParamBlock, EjectTheCartridge); } break; case kEjectCartridgeDone: { AbsoluteTime theWait; StorageExecuteCommandPB *commandPB; DriveRec *drive; drive = &ourPB->theDrive; RemoveDrive(drive); commandPB = &ourPB->drivePB.executePB; // use a pointer to the executePB field ourPB->drivePB.status = commandPB->status; if(commandPB->status == noErr) { ourPB->currentExecutionState = kMountStartState; // reset the machine theWait = DurationToAbsolute(durationSecond); theWait = AddAbsoluteToAbsolute(UpTime(), theWait); SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer); } drive->capacity = 0; drive->blockSize = 0; ourPB->isFloppy = false; ourPB->diskInDrive = false; } break; } } OSStatus FormatCompletionInterrupt( void *p1, void *p2) { #pragma unused ( p1, p2 ) gInterruptTimer = 0; gTheParamBlock.currentExecutionState = kFormatWaitDoneState; FormatTheCartridge( &gTheParamBlock ); return noErr; } void FormatTheCartridge( void *theCurrentPB ) { OSStatus err = noErr; AbsoluteTime oneSecondWait; DriveRec *drive; OurParamBlock *ourPB; ourPB = ((OurParamBlock *) theCurrentPB); drive = &ourPB->theDrive; oneSecondWait = DurationToAbsolute(durationSecond); switch (ourPB->currentExecutionState ) { case kFormatStartState: { ourPB->currentExecutionState = kFormatDoneState; err = FormatFloppyCartridge( ourPB, &FormatTheCartridge); } break; case kFormatDoneState: { AbsoluteTime theWait; theWait = DurationToAbsolute(durationSecond*35); theWait = AddAbsoluteToAbsolute(UpTime(), theWait); SetInterruptTimer( &theWait, &FormatCompletionInterrupt, nil, &gInterruptTimer); err = 1; } break; case kFormatWaitDoneState: { ourPB->currentExecutionState = kFormatRequestSenseDoneState; ourPB->drivePB.retryCount = 0; err = RequestSense( ourPB, &FormatTheCartridge); } break; case kFormatRequestSenseDoneState: { //SysDebugStr("\pRequest is done"); if(ourPB->drivePB.executePB.status == noErr) { if(ourPB->sense[12] == 0x00) { ourPB->currentExecutionState = kFormatGetGeometryDoneState; ourPB->getCapacity.lastLogicalBlock = 0; ourPB->getCapacity.blockLength = 0; err = GetMediaGeometry( ourPB, &FormatTheCartridge); } else { // The format has failed, inform the OS err = controlErr; } } else { if (ourPB->drivePB.retryCount < kFormatRequestSenseRetryCount) { ourPB->currentExecutionState = kFormatRequestSenseDoneState; ourPB->drivePB.retryCount += 1; err = RequestSense( ourPB, &FormatTheCartridge); } else { // The format has failed, inform the OS ourPB->drivePB.retryCount = 0; err = controlErr; } } } break; case kFormatGetGeometryDoneState: { ReadCapacityData *getGeometry; getGeometry = (ReadCapacityData *) &ourPB->getCapacity; drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in drive->blockSize = getGeometry->blockLength; if( (ourPB->drivePB.executePB.status != noErr) || (drive->capacity == 0) || (drive->blockSize == 0)) { // The format has failed, inform the OS err = controlErr; break; } if( drive->capacity >0x0C00) { ourPB->isFloppy = false; } else { ourPB->isFloppy = true; } ourPB->diskInDrive = true; } break; } ourPB->drivePB.status = err; FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status); } OSStatus ReinsertionSecondaryInterrupt( void *p1, void *p2) { #pragma unused ( p1, p2 ) gInterruptTimer = 0; gTheParamBlock.currentExecutionState = kReinsertionCheckStart; CheckForCartridgeReinsertion( &gTheParamBlock ); return noErr; } void CheckForCartridgeReinsertion( void *theCurrentPB ) { OSStatus err = noErr; AbsoluteTime oneSecondWait; DriveRec *drive; OurParamBlock *ourPB; ourPB = ((OurParamBlock *) theCurrentPB); drive = &ourPB->theDrive; oneSecondWait = DurationToAbsolute(durationSecond); switch (ourPB->currentExecutionState ) { case kReinsertionCheckStart: { ourPB->currentExecutionState = kReinsertionTURDoneState; err = TUR( ourPB, &CheckForCartridgeReinsertion); } break; case kReinsertionTURDoneState: { ourPB->currentExecutionState = kReinsertionRequestSenseDone; err = RequestSense( ourPB, &CheckForCartridgeReinsertion); } break; case kReinsertionRequestSenseDone: { if(ourPB->sense[12] == 0x00) { ourPB->diskInDrive = true; } else { ourPB->currentExecutionState = kReinsertionCheckStart; oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait); err = SetInterruptTimer( &oneSecondWait, &ReinsertionSecondaryInterrupt, nil, &gInterruptTimer); err = noErr; } } break; } } OSStatus CheckWriteProtect(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion) { OSStatus status = noErr; IfDebugging("\pCheck Write Protect"); if (gItsTheDispatchTable) { StorageExecuteCommandPB *commandPB; commandPB = &theCurrentPB->drivePB.executePB; // use a pointer to the executePB field BlockZero(commandPB, sizeof(StorageExecuteCommandPB)); // clear out the PB we will send BlockZero((Ptr) &theCurrentPB->sense[0], 8); commandPB->cdb[0] = kCmdModeSense; commandPB->cdb[8] = 0x08; commandPB->flags = kStorageDataIn; // -> Expect a data in transfer commandPB->userBuffer = (Ptr) &theCurrentPB->sense[0]; // -> Pointer to user buffer commandPB->expectedCount = 8; // -> Expected number of bytes to transfer commandPB->completionProc = ourCompletion; // -> Completion routine status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB); } return status; } OSStatus TUR(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion) { OSStatus status = noErr; IfDebugging("\pTUR"); if (gItsTheDispatchTable) { StorageExecuteCommandPB *commandPB; commandPB = &theCurrentPB->drivePB.executePB; // use a pointer to the executePB field BlockZero(commandPB, sizeof(StorageExecuteCommandPB)); // clear out the PB we will send commandPB->flags = kStorageNoData; commandPB->completionProc = ourCompletion; status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB); } return status; } OSStatus RequestSense(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion) { OSStatus status = noErr; if (gItsTheDispatchTable) { StorageExecuteCommandPB *commandPB; commandPB = (StorageExecuteCommandPB *)&theCurrentPB->drivePB.executePB; // use a pointer to the executePB field BlockZero(commandPB, sizeof(StorageExecuteCommandPB)); BlockZero((Ptr) &theCurrentPB->sense[0], sizeof(kSenseDataSize)); commandPB->cdb[0] = kCmdRequestSense; commandPB->cdb[4] = kSenseDataSize; commandPB->flags = kStorageDataIn; // -> Expect a data in transfer commandPB->userBuffer = (Ptr) &theCurrentPB->sense[0]; // -> Pointer to user buffer commandPB->expectedCount = kSenseDataSize; // -> Expected number of bytes to transfer commandPB->completionProc = ourCompletion; // -> Completion routine status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB); } return status; } OSStatus GetMediaGeometry(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion) { OSStatus status = noErr; IfDebugging("\pGetMediaGeometry"); if (gItsTheDispatchTable) { ReadCapacityData *getGeometry; StorageExecuteCommandPB *commandPB; commandPB = &theCurrentPB->drivePB.executePB; // use a pointer to the executePB field getGeometry = &theCurrentPB->getCapacity; getGeometry->lastLogicalBlock = 0; getGeometry->blockLength = 0; BlockZero(commandPB, sizeof(StorageExecuteCommandPB)); commandPB->cdb[0] = kCmdReadCapacity; commandPB->flags = kStorageDataIn; // -> Expect a data in transfer commandPB->userBuffer = (Ptr) getGeometry; // -> Pointer to user buffer commandPB->expectedCount = sizeof(ReadCapacityData); // -> Expected number of bytes to transfer commandPB->completionProc = ourCompletion; // -> Completion routine status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB); } return status; } OSStatus ReadFormatCapacity(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion) { OSStatus status = noErr; IfDebugging("\pReadFormatCapacity"); if (gItsTheDispatchTable) { StorageExecuteCommandPB *commandPB; commandPB = &theCurrentPB->drivePB.executePB; // use a pointer to the executePB field BlockZero(commandPB, sizeof(StorageExecuteCommandPB)); commandPB->cdb[0] = kCmdReadFormatCapacities; commandPB->cdb[7] = 0; commandPB->cdb[8] = 0x0C; commandPB->flags = kStorageDataIn; // -> Expect a data in transfer commandPB->userBuffer = (Ptr) &theCurrentPB->sense[0]; // -> Pointer to user buffer commandPB->expectedCount = 0x0C; // -> Expected number of bytes to transfer commandPB->completionProc = (StorageClassCompletionProcPtr) ourCompletion; // -> Completion routine status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB); } return status; } OSStatus FormatFloppyCartridge(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion) { OSStatus status = noErr; IfDebugging("\pFormatFloppyCartridge"); if (gItsTheDispatchTable) { StorageExecuteCommandPB *commandPB; commandPB = &theCurrentPB->drivePB.executePB; // use a pointer to the executePB field BlockZero(commandPB, sizeof(StorageExecuteCommandPB)); BlockZero((Ptr) &theCurrentPB->sense[0], 0x0C); // Set up the data going out // First set up the Defect List Header theCurrentPB->sense[0] = 0; //theCurrentPB->sense[1] = 0x82; theCurrentPB->sense[1] = 0; theCurrentPB->sense[2] = 0; theCurrentPB->sense[3] = 0x08; *((UInt32 *) &theCurrentPB->sense[4]) = theCurrentPB->theDrive.capacity; *((UInt16 *) &theCurrentPB->sense[10]) = theCurrentPB->theDrive.blockSize; commandPB->cdb[0] = kCmdFormat; commandPB->cdb[1] = 0x17; commandPB->flags = kStorageDataOut; // -> Expect a data out transfer commandPB->userBuffer = (Ptr) &theCurrentPB->sense[0]; // -> Pointer to user buffer commandPB->expectedCount = 0x0C; // -> Expected number of bytes to transfer commandPB->completionProc = (StorageClassCompletionProcPtr) ourCompletion; // -> Completion routine #if 0 BlockZero(commandPB, sizeof(StorageExecuteCommandPB)); commandPB->cdb[0] = 0x24; commandPB->cdb[6] = (((theCurrentPB->theDrive.blockSize >> 8) & 0x07) << 4) | 8; commandPB->flags = kStorageNoData; // -> Expect a data out transfer commandPB->userBuffer = nil; // -> Pointer to user buffer commandPB->completionProc = (StorageClassCompletionProcPtr) ourCompletion; // -> Completion routine #endif //SysDebugStr("\pBreak for format"); status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB); } return status; } OSStatus EjectCartridge(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion) { OSStatus status = noErr; if (gItsTheDispatchTable) { StorageExecuteCommandPB *commandPB; commandPB = &theCurrentPB->drivePB.executePB; // use a pointer to the executePB field BlockZero(commandPB, sizeof(StorageExecuteCommandPB)); commandPB->cdb[0] = kCmdStartStopUnit; commandPB->cdb[4] = 0x02; // Unload the Media commandPB->flags = kStorageNoData; // -> Expect a data in transfer commandPB->userBuffer = nil; // -> Pointer to user buffer commandPB->expectedCount = 0; // -> Expected number of bytes to transfer commandPB->completionProc = (StorageClassCompletionProcPtr) ourCompletion; // -> Completion routine status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB); } return status; } OSStatus PreventAllowRemoval(OurParamBlock *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion) { OSStatus status = noErr; if (gItsTheDispatchTable) { StorageExecuteCommandPB *commandPB; commandPB = &theCurrentPB->drivePB.executePB; // use a pointer to the executePB field BlockZero(commandPB, sizeof(StorageExecuteCommandPB)); commandPB->cdb[0] = kCmdPreventAllowRemoval; if ( preventRemoval == true ) { commandPB->cdb[4] = 0x01; // Prevent Media Removal } else { commandPB->cdb[4] = 0x00; // Allow Media Removal } commandPB->flags = kStorageNoData; // -> Expect a data in transfer commandPB->userBuffer = nil; // -> Pointer to user buffer commandPB->expectedCount = 0; // -> Expected number of bytes to transfer commandPB->completionProc = (StorageClassCompletionProcPtr) ourCompletion; // -> Completion routine status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB); } return status; } /* ---------------------Supporting Functions below this point -------------------------- */ //------------------------------------------------------------------------------ // Function: DRVRPrime // Description: This is the ATA driver PRIME call that performs // reading and writing to the device. // // Input: ioPB = Pointer to caller's I/O parameter block // dce = Pointer to Device Control Entry (DCE) // Output: A status code is returned. //------------------------------------------------------------------------------- OSStatus DoReadWriteCommand( OurParamBlock *theDriverPB, Boolean doWrite ) { OSStatus err; UInt32 startingBlock, numBlocks; VolumeRecPtr vol; IOParamPtr iopb; DriveRecPtr drive; drive = &theDriverPB->theDrive; iopb = (IOParamPtr) theDriverPB->drivePB.theIOPB; // Find the associated volume and drive records required for the request. vol = GetVolume(drive, iopb->ioVRefNum, 0); // Get the volume record requested //if (vol) // if we have a volume // drive = (DriveRecPtr)vol->drivePtr; // get its drive record //else // assume request if for a physical drive if (!vol) // if we have a volume { if (drive) // if physical drive matches… { vol = drive->nextVol; if (vol) // and a volume exists for it… { if (vol->curoffset) // and not doing physical addressing… { vol = 0; // the volume is invalid } } } } if (!vol || !drive) // Abort if we don't have both drive and volume records return(nsDrvErr); if (drive->busState != kONLINE) // drive must be connected also! return(nsDrvErr); //................................................................................ // A usable drive and volume exists. Continue processing the request… // Compute the starting block address for the request. We accept // only the normal 32 bit address and not the new 'Large Volume (64 bit) Addressing'. //SysDebugStr("\pCalculate the Read."); startingBlock = (UInt32)((iopb->ioPosOffset)/(drive->blockSize)); numBlocks = (iopb->ioReqCount)/(drive->blockSize); if (doWrite && vol->driveStatus.writeProt) { // check for write protect err = wPrErr; } else if (iopb->ioReqCount & ((drive->blockSize) - 1)) { // Verify if request is a multiple of the drives blocksize err = paramErr; } else if ( ( startingBlock + numBlocks ) > ((vol->curoffset) ? vol->partblks : drive->capacity)) { // Verify if request is within range with respect to doing physical or logical I/O. // Access is limited to partition range (logical I/O) if curoffset is non-zero. err = paramErr; } else if ((iopb->ioPosMode & rdVerify) && !doWrite) { // If Read-Verify mode, this is not efficient with disks so we simply pretend we did it. iopb->ioActCount = iopb->ioReqCount; iopb->ioPosOffset += iopb->ioActCount; err = noErr; } else // Do the read or write { startingBlock += vol->curoffset; // add in the partition offset err = ReadWriteBlock( theDriverPB, startingBlock, numBlocks, iopb->ioBuffer, doWrite, true ); } return err; } //------------------------------------------------------------------------------ // Function: ReadWriteBlock // // Description: Low level read/write block on the media with retries. // // Input: drive: Pointer to physical drive record // blockAddr: Starting block address // numBlocks: Number of blocks to read/write // buffer: Pointer to buffer // doWrite: 1 = write, 0 = read // // Output: true if successful, false if not //------------------------------------------------------------------------------- OSStatus ReadWriteBlock( OurParamBlock *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync) { StorageExecuteCommandPBPtr theReadWriteRequest; UInt32 driveBlockSize; volatile OSStatus status = noErr; IfDebugging("\p…ReadWriteBlock"); theDriverPB->drivePB.status = noErr; driveBlockSize = theDriverPB->theDrive.blockSize; theReadWriteRequest = &(theDriverPB->drivePB.executePB); BlockZero(theReadWriteRequest, sizeof(StorageExecuteCommandPB)); // clear out the PB we will send theReadWriteRequest->userBuffer = buffer; // -> Pointer to user buffer theReadWriteRequest->expectedCount = numBlocks*driveBlockSize; // -> Expected number of bytes to transfer theReadWriteRequest->completionProc = (StorageClassCompletionProcPtr) ReadWriteCompletion; // -> Completion routine theReadWriteRequest->actualCount = 0; // <- Actual number of bytes transferred theReadWriteRequest->status = 0; // <- Result of operation if(doWrite) { theReadWriteRequest->cdb[0] = kCmdWrite; theReadWriteRequest->flags = kStorageDataOut; // -> Expect a data out transfer } else { theReadWriteRequest->cdb[0] = kCmdRead; theReadWriteRequest->flags = kStorageDataIn; // -> Expect a data in transfer } // Set the starting block in the CDB theReadWriteRequest->cdb[2] = (startBlock >> 24) & 0xff; theReadWriteRequest->cdb[3] = (startBlock >> 16) & 0xff; theReadWriteRequest->cdb[4] = (startBlock >> 8) & 0xff; theReadWriteRequest->cdb[5] = startBlock & 0xff; // Set the Block Count in the CDB theReadWriteRequest->cdb[7] = (numBlocks >> 8) & 0xff; theReadWriteRequest->cdb[8] = numBlocks & 0xff; status = ((gItsTheDispatchTable)->pStorageExecuteCmd)( theReadWriteRequest ); theDriverPB->drivePB.status = status; if(status != 1) { theDriverPB->drivePB.theIOPB = nil; } else { if(doAsync == false) { while ( status == 1 ) { status = theDriverPB->drivePB.status; } } } return status; } void ReadWriteCompletion( void *theDriverPB ) { OSStatus status; OurParamBlock *ourPB; Boolean wasWrite; //SysDebugStr("\pReadWriteCompletion;g"); ourPB = (OurParamBlock *) theDriverPB; status = ourPB->drivePB.executePB.status; wasWrite = ((ourPB->drivePB.executePB.flags & kStorageDataOut) == kStorageDataOut); if(ourPB->doInternalReadWrite == false) { IOParamPtr iopb; iopb = (IOParamPtr) ourPB->drivePB.theIOPB; if( status == noErr ) { if( wasWrite == true ) { // It was a write, do a Request sense to determine if any // errors occurred. status = RequestSense( ourPB, &WriteRequestSenseCompletion); if( status != 1) { // An error occurred while trying to do the Request sense, // return an ioErr to the system. iopb->ioActCount = 0; // Set the status in the DriverPB last, this way if there is an immediate command, // It won't think the command is done till after our processing. ourPB->drivePB.status = ioErr; // Signal completion of the command to the operating system ourPB->drivePB.theIOPB = nil; FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status); } } else { // This was a read command, and no errors occurred, finish the IO request and // return to the system. iopb->ioActCount = iopb->ioReqCount; // Set the status in the DriverPB last, this way if there is an immediate command, // It won't think the command is done till after our processing. ourPB->drivePB.status = noErr; // Signal completion of the command to the operating system ourPB->drivePB.theIOPB = nil; FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status); } } else { // An error occurred on the command, do a Request sense to be certain any // USB Device stalls are cleared. status = RequestSense( ourPB, &RequestSenseOnErrorCompletion); if( status != 1) { // An error occurred while trying to do the Request sense, // return an ioErr to the system. iopb->ioActCount = 0; // Set the status in the DriverPB last, this way if there is an immediate command, // It won't think the command is done till after our processing. ourPB->drivePB.status = ioErr; // Signal completion of the command to the operating system ourPB->drivePB.theIOPB = nil; FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status); } } } else { // If this is an internal command, this is all we care about ourPB->drivePB.status = status; } IfDebugging("\p…ReadWriteBlock Done"); } void WriteRequestSenseCompletion( void *theDriverPB ) { OSStatus status; OurParamBlock *ourPB; IOParamPtr iopb; ourPB = (OurParamBlock *) theDriverPB; status = ourPB->drivePB.executePB.status; iopb = (IOParamPtr) ourPB->drivePB.theIOPB; if( status == noErr ) { if( ( ourPB->sense[2] & 0x0F !=0 ) && ( ourPB->sense[2] & 0x0F !=1 )) { // An error has been reported back in the sense key, return // an ioErr to the system. iopb->ioActCount = 0; ourPB->drivePB.status = ioErr; } else { // No errors has been reported back in the sense key, return // a noErr to the system. iopb->ioActCount = iopb->ioReqCount; ourPB->drivePB.status = noErr; } } else { // Errors occurred on the Request sense report an // ioErr back to the system iopb->ioActCount = 0; ourPB->drivePB.status = ioErr; } // Signal completion of the command to the operating system //SysDebugStr("\pWriteReqSenseFinishCommand"); ourPB->drivePB.theIOPB = nil; FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status); } void RequestSenseOnErrorCompletion( void *theDriverPB ) { OurParamBlock *ourPB; IOParamPtr iopb; ourPB = (OurParamBlock *) theDriverPB; iopb = (IOParamPtr) ourPB->drivePB.theIOPB; // An error occurred on the initial command, and no error occurred on the RequestSense if( (ourPB->drivePB.retryCount < kReadWriteRetryCount) && (ourPB->drivePB.executePB.status == noErr)) { OSStatus err; // We have not yet exceeded the retry count, so try the operation again // Check to see if the disk was manually removed (using the emergency pinhole) if(((ourPB->sense[2] & 0x0F) == 0x02) && (ourPB->sense[12] == 0x3A)) { AbsoluteTime theWait; //SysDebugStr("\pWhere did yo Go?"); ourPB->diskInDrive = false; ourPB->theDrive.busState = kOFFLINE; CheckUnexpectedRemoval(&ourPB->theDrive); RemoveDrive(&gTheParamBlock.theDrive); theWait = DurationToAbsolute(durationSecond); theWait = AddAbsoluteToAbsolute(UpTime(), theWait); (void) SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer); err = ioErr; } else { // Increment our retry counter ourPB->drivePB.retryCount += 1; // Send the command out again err = DoReadWriteCommand( ourPB, ourPB->drivePB.doWrite); } if ( err !=1 ) { iopb->ioActCount = 0; ourPB->drivePB.status = ioErr; // Signal completion of the command to the operating system ourPB->drivePB.theIOPB = nil; FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status); } } else { // Since we only get here if an error occurred on the orignal // command, and the Request Sense was to clear any device stalls. // We will always report back an ioErr to the system. iopb->ioActCount = 0; ourPB->drivePB.status = ioErr; // Signal completion of the command to the operating system ourPB->drivePB.theIOPB = nil; FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status); } } //------------------------------------------------------------------------------ // Function: GetVolume // // Description: Searches for the volume record with the specified vRefNum and // returns its VolumeRecPtr. Optionally, partitionNum // can be use as search keys if vRefNum is zero. // // Input: drive: the pointer to the DriveRec // vRefNum: the volume's reference number // partitionNum: the volume's partition number // // Output: pointer to volume record if found, nil if not found //------------------------------------------------------------------------------- VolumeRecPtr GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum) { VolumeRecPtr vol = nil; Boolean found; // Search all volume records for one matching the search key vol = drive->nextVol; // first volume pointer while (vol) { found = (vRefNum) ? (vol->vRefNum == vRefNum) : (vol->partitionNo == partitionNum); if (found) return(vol); // found the partition else vol = (VolumeRecPtr)vol->nextVol; // otherwise, get next volume pointer } return(vol); // nil if volume not found } //------------------------------------------------------------------------------ // Function: CreateVolume // // Description: This function creates a volume record for the specified drive. // The volume is appended to the drive's volume queue and a logical // logical drive is installed in the system drive queue. It is // assumed the volume is not write protected, drive is installed, // and the media is installed. // // Input: drive: pointer to the drive record of the volume to create // partitionID: the volume's partition ID // volSize: size of the volume in blocks // volOffset: block offset of volume on drive // // Output: Returns nil pointer if fails, else pointer to volume record // // NOTE: Assumes all inputs are valid! //------------------------------------------------------------------------------- VolumeRecPtr CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset ) { register VolumeRecPtr vol; VolumeRecPtr* volHandle; Boolean volInQueue = false; // When a volume is offlined by the system (greyed desktop icon) it is still // being used and its associated DrvQEl must remain. Thus, its volume and // physical drive record also must remain. So, when new media is inserted // we need to check for these structures and reuse them before creating new ones. // The structures can be reused since the file system verifies the volume. if( drive->nextVol == nil) { // Make sure the drive's volume queue points to the volume vol = &gTheParamBlock.theVolume; } else { // Search for a volume record for the volume (partition) to be created… vol = GetVolume(drive, 0, partitionID); if (vol) // if record exists for this volume… { volInQueue = true; // remember volume is already in queue if (vol->driveStatus.diskInPlace) // if volume is already online… vol = nil; // we shouldn't be here - fall thru } else // need to create volume record { vol = (VolumeRecPtr) PoolAllocateResident(sizeof(VolumeRec), true); // Allocate storage for record //vol = (VolumeRecPtr) NewPtrSysClear(sizeof(VolumeRec)); // Allocate storage for record } } if (vol) // if a volume record is valid… { if( gTheParamBlock.isFloppy == true) { vol->driveStatus.track = 80; // Sectors on a MFM disk vol->driveStatus.sides = -1; // -1 means double sided floppy } else { vol->driveStatus.track = 0; // Only used on floppys vol->driveStatus.sides = 0; // Only used on floppys } if( gTheParamBlock.isWriteProtected == true) { vol->driveStatus.writeProt = 0x80; // disk is write protected } else { vol->driveStatus.writeProt = 0; // not write protected yet } vol->driveStatus.diskInPlace = 1; // Ejectable Disk vol->driveStatus.installed = 1; // drive is installed vol->driveStatus.qType = 1; // both dQDrvSz and dQDrvSz2 are used vol->driveStatus.dQFSID = 0; // File Manager's volume type vol->driveStatus.driveSize = (UInt16) volSize; // volume size in blocks vol->driveStatus.driveS1 = (UInt16) (volSize >> 16); vol->mountthispart = false; // don't mount this volume, vol->partmounted = false; // it's not mounted yet, vol->partitionNo = partitionID; // save the partition ID // Save volume's block offset and set its access mode by setting curoffset to the // same (access is relative to partition offset if curoffset is non-zero, else physical) vol->curoffset = volOffset; // block offset of volume vol->partoffset = volOffset; // partition offset vol->partblks = volSize; // save size for our use also vol->drivePtr = (Ptr) drive; // pointer to vol's physical drive if( gTheParamBlock.isFloppy == true) { DrvSts *theDriveStatus; theDriveStatus = (DrvSts *) &vol->driveStatus; theDriveStatus->twoSideFmt = -1; /* after 1st rd/wrt: 0=1 side, -1=2 side */ theDriveStatus->needsFlush = -1; /* -1 for MacPlus drive */ theDriveStatus->diskErrs = 0; /* soft error count */ vol->mediaIconPtr = (Ptr) AppleFloppyMediaIcon; // media icon for floppies } else { vol->mediaIconPtr = (Ptr) &CartridgeIcon; // media icon for cartridge } // If this record is not in our volume list yet, finish initializing and insert if (!volInQueue) // If not in the volume queue… { vol->driveStatus.qLink = nil; // initialize system queue link vol->nextVol = nil; // no link to next volume yet // Find the end of the drive's volume queue and link in the new volume record. volHandle = &(drive->nextVol); // point to start of drive's volume queue while (*volHandle) // search for end of volume queue… { volHandle = (VolumeRecPtr*)&((*volHandle)->nextVol); } *volHandle = vol; // at end of queue, insert volume } drive->numVolumes++; // update number of drive volumes } return(vol); } //------------------------------------------------------------------------------ // Function: InstallVolumes // Description: Searches for partitions on the media and installs them as // volumes for the associated drive. Assumes the drive does // not have any volumes installed yet. // // Input: theDrive: pointer to drive record // mountVols: true means to mount the drive's partitions // //------------------------------------------------------------------------------- void InstallVolumes(DriveRecPtr theDrive, Boolean mountVols) { VolumeRecPtr vol = nil; // If no partitions were found from the search above we assume the following: // 1. A Macintosh partition map exists, but it has either no file system partitions // or no partitions which we recognize (either or which is unlikely). // 2. The media has a Macintosh floppy format (HFS with no partition map) // 3. The media has a foreign format (DOS, etc.). // // In all cases we create a volume of the entire media capacity, post a disk inserted // event and let the File System Manager try and figure it out. Note we post a disk // inserted event rather than notifying FSM so if the media is not recognized (because // it's unformatted or the correct file system is not installed) the system will prompt // with a "This is not a Macintosh disk…" message. If we call FSM instead, the user will // not be prompted when the media is unformatted. This provides a way to format media // and also allows us to eject PCMCIA drives which have no volumes (no icons on desktop). if (theDrive->numVolumes == 0) // if no partitions were found… { IfDebugging("\ptheDrive->numVolumes == 0"); vol = CreateVolume(theDrive, 1, theDrive->capacity, 0); if (vol) vol->mountthispart = true; // post disk inserted event later } // Add the remaining volumes to the drive queue… vol = theDrive->nextVol; // first volume of the drive while (vol) { if ((vol->mountthispart == true) || (mountVols == false)) { UInt16 volNumber; vol->vRefNum = NextQDrive(); // assign a logical drive number volNumber = vol->vRefNum; // Check to see if we need to add one to the AddDrive call if( gTheParamBlock.addOneToAddDrive == true) { volNumber += 1; } AddDrive(gTheParamBlock.drvrRefNum, volNumber,(DrvQElPtr) &vol->driveStatus.qLink); } vol = (VolumeRecPtr)vol->nextVol; // point to the next volume } } //------------------------------------------------------------------------------ // Function: RemoveVolume // Description: Deletes a volume and its record // Input: drvRec: pointer to physical drive record of volume // volRef: Volume reference //------------------------------------------------------------------------------- void RemoveVolume(DriveRecPtr drvRec, UInt16 volRef) { VolumeRecPtr* pvHandle = &(drvRec->nextVol); VolumeRecPtr pvPtr = *pvHandle; while (pvPtr) { if (pvPtr->vRefNum == volRef) // found the volume record { Dequeue((QElemPtr) &(pvPtr->driveStatus.qLink), GetDrvQHdr()); // remove from drive queue *pvHandle = (VolumeRecPtr)(pvPtr->nextVol); // remove it from the linked list if (pvPtr == &gTheParamBlock.theVolume) { BlockZero((Ptr) &gTheParamBlock.theVolume, sizeof(VolumeRec)); } else { PoolDeallocate( pvPtr ); // release its memory //DisposePtr((Ptr) pvPtr ); // release its memory } drvRec->numVolumes--; // decrement number of drive volumes break; } pvHandle = (VolumeRecPtr*)&(pvPtr->nextVol); // save pointer to pv pointer pvPtr = (VolumeRecPtr)pvPtr->nextVol; // point to next pv } } //------------------------------------------------------------------------------ // Function: InstallDrive // // Description: Installs a physical drive and its volumes under the driver's // control. The driver determines if the drive is one it can // manage, and if so, creates and initializes the drive's record, // sets the drives operating mode and options, and mounts its // partitions to the system. // // Input: drvNum: physical drive reference // mountVols: true means to mount drive's partitions // // Output: true if successful, false if not //------------------------------------------------------------------------------- Boolean InstallDrive(DriveRecPtr theDrive, Boolean mountVols) { // Initialize variables associated with the new drive. theDrive->isSpunDown = false; // drive is spinning (full power) theDrive->inSleepMode = false; // not sleeping theDrive->doSpindown = false; // no spindown timer yet theDrive->numVolumes = 0; // no partitions yet theDrive->busState = kONLINE; // drive is online //.............................................................................. // Search for file system partitions on the media and install them as volumes // of this drive. If no volumes, the drive must be considered unusable. InstallVolumes(theDrive, mountVols); if (theDrive->numVolumes) { OSErr theErr; theErr = MountUnmountVolumes( theDrive ); if(theErr != noErr) { // For some reason, the disk could not be mounted, // We should eject and let the user decide whether to try again. gTheParamBlock.currentExecutionState = kEjectStartState; EjectTheCartridge( &gTheParamBlock ); } } else // Abort if no volumes installed for drive { return(false); } return(true); } //------------------------------------------------------------------------------ // Function: RemoveDrive // Description: Removes a physical drive and its volumes from our control // //------------------------------------------------------------------------------- void RemoveDrive(DriveRecPtr theDrivePtr) { VolumeRecPtr vol; UInt16 vrefnum; if (theDrivePtr) // If the drive exists… { while (theDrivePtr->nextVol) // dequeue all volumes on the drive… { vol = theDrivePtr->nextVol; // get volume vrefnum = vol->vRefNum; // get its refnum RemoveVolume(theDrivePtr, vrefnum); // delete volume from our queue } } #if 0 RemovePowerManagement(); // Remove power management routines and VBLs #endif } //------------------------------------------------------------------------------ // Function: NextQDrive // // Description: Returns the next unused logical drive number from the system // Drive Queue. The Drive Queue is searched starting with a // logical drive number 8. // // Input: none // // Output: The highest Drive Queue drive number + 1 //------------------------------------------------------------------------------- SInt16 NextQDrive( void ) { QHdrPtr qhdr = GetDrvQHdr(); // Pointer to Drive Queue DrvQEl *qel = (DrvQEl*) (qhdr->qHead); // Pointer to first element SInt16 drv = 8; // Start above built in drives while (qel) // While not end of queue, { if (qel->dQDrive == drv) // if drive number used, { drv++; // bump number, and qel = (DrvQEl*) (qhdr->qHead); // search from start } else // else next queue element qel = (DrvQEl*) (qel->qLink); } return(drv); // Return the next logical drive } //------------------------------------------------------------------------------ // Function: FlushDriveWriteCache // // Description: Flushes the drive's write cache // // Input: None. // // Output: None. //------------------------------------------------------------------------------- void FlushDriveWriteCache( void ) { // Unfortunately, there is no consistent method for flushing the write cache on // all drives. However, all drives should flush the cache within a couple of // rotations so we can simply hang out for a bit and hope the cache is flushed. // Use a non-interrupt timer since interrupts may be disabled at this time. AbsoluteTime theWait; theWait = DurationToAbsolute(durationMillisecond*500); DelayForHardware( theWait ); // delay for max seek and a few rotations (500 msec.) } //------------------------------------------------------------------------------ // Function: FindMountedVol // Description: Searches the volume queue for a mounted volume specified // by vRefNum. If one is found, its VCB pointer is returned. // // Input: vRefNum: the volume reference to search for // // Output: the VCB pointer (NULL = volume not mounted) //------------------------------------------------------------------------------- static VCB *FindMountedVol(SInt16 vRefNum) { QHdrPtr volQ = GetVCBQHdr(); // VCB queue head pointer VCB *theVol = (volQ) ? (VCB*)volQ->qHead : 0; // first VCB while(theVol) { // The test for whether a volume is mounted or not is done using // the VCB fields vcbDrvNum and vcbDRefNum. A volume is mounted // only if it is online. // // online offline ejected // // vcbDrvNum >0 (DrvNum) 0 0 // vcbDRefNum <0 (DRefNum) <0 (-DrvNum) >0 (DrvNum) if (theVol->vcbDrvNum == vRefNum) // if volume specified is online… break; // volume is mounted theVol = (VCB*)theVol->qLink; // next VCB } return(theVol); } //------------------------------------------------------------------------------ // Function: MountedVolOfDrive // // Description: Searches the system VCB for a mounted volume on the specified // physical drive. // // Input: drive: pointer to the physical drive's record // // Output: the VCB pointer (nil if no volume mounted for drive) //------------------------------------------------------------------------------- VCB * MountedVolOfDrive(DriveRecPtr drive) { VolumeRecPtr vol = nil; VCB *vcb = nil; if (drive) { vol = drive->nextVol; // first volume (logical drive) of drive while (vol) { vcb = FindMountedVol(vol->vRefNum); // check if a volume is mounted if (vcb) // if mounted volume, stop now break; else // next volume of drive vol = (VolumeRecPtr)vol->nextVol; } } return(vcb); } //------------------------------------------------------------------------------ // Function: UpdateQ // Description: Updates the specified drive in the system drive queue with // the specified capacity // // Input: qDrive: the drive to update // newSize: the new drive capacity //------------------------------------------------------------------------------- void UpdateQ(SInt16 qDrive, SInt32 newSize) { QHdrPtr qhdr = GetDrvQHdr(); // Pointer to Drive Queue DrvQEl *qel = (DrvQEl*) (qhdr->qHead); // Pointer to first element while (qel) { // Search until drive is found, then update its capacity if (qel->dQDrive == qDrive) // if drive number found, { qel->dQDrvSz2 = *(UInt16*)&newSize; // new capacity (hi word) qel->dQDrvSz = newSize; // low word of capacity break; } else // else next queue element qel = (DrvQEl*) (qel->qLink); } } //------------------------------------------------------------------------------ // FUNCTION: NextPartitionID // PURPOSE: Returns the next unique partition ID for all volumes associated // with the specified drive. NOTE: This function should be used only // when adding volumes which do not have a partition map on the media. // // INPUT: drive: pointer to the drive record to search for next partition ID // // OUTPUT: a unique partition ID related to the specified volume // //------------------------------------------------------------------------------- SInt32 NextPartitionID(DriveRecPtr drive) { VolumeRecPtr vol = 0; SInt32 nextPartitionID = 1; // Start search with partition ID of 1 if (drive) // if drive is present… { vol = (VolumeRecPtr)drive->nextVol; // first volume pointer while (vol) // while not end of volume queue… { if (vol->partitionNo == nextPartitionID) // if partition ID used { nextPartitionID++; // bump partition ID vol = drive->nextVol; // reset volume pointer } else vol = (VolumeRecPtr)vol->nextVol; // otherwise, point to next volume } } return(nextPartitionID); // nil if volume not found } //------------------------------------------------------------------------------ // FUNCTION: SetPowerMode // // PURPOSE: Sets the power mode on the drive per the New Driver Rules. The // mode is translated to the closest ATA power mode. // // INPUT: drive: The physical drive number // powerMode 0 = kMediaModeOn (normal operation) // 1 = kMediaModeStandBy (reduced power, ready in 5 sec.) // 2 = kMediaModeSuspend: (minimal power, ready in 15 sec.) // 3 = kMediaModeOff: (no power) // // OUTPUT: Returns true if successful // // NOTES: Assumes drive is valid value (drive is present and online) //------------------------------------------------------------------------------ Boolean SetPowerMode(DriveRecPtr drive, UInt16 powerMode, UInt16 timerSecs) { #pragma unused ( drive, powerMode, timerSecs) IfDebugging("\pSetPowerMode"); return noErr; #if 0 // Array to map power modes to ATA power commands. The mapping is as follows: // // 0 (kMediaModeOn) = Idle Immediate (reduced power, ready, spinning) // 1 (kMediaModeStandBy) = Idle Immediate (reduced power, ready, spinning) // 2 (kMediaModeSuspend) = Standby Immediate (lower power, ready, not spinning) // 3 (kMediaModeOff) = Sleep (minimal power, not ready, not spinning) UInt16 err; UInt8 PowerCommand[6] = {kATAcmdIdleImmed, kATAcmdIdleImmed, kATAcmdStandbyImmed, kATAcmdSleep, kATAcmdIdle, kATAcmdStandby}; // Make call to device here // Set drive status depending upon the power mode command given if (err == noErr) switch(powerMode) { case kMediaModeOn: case kMediaModeStandBy: drive->isSpunDown = false; // drive is spinning break; case kMediaModeSuspend: drive->isSpunDown = true; // drive is not spinning break; case kMediaModeOff: drive->isSpunDown = true; // drive is not spinning drive->inSleepMode = true; // flag sleep mode since we can't check if drive is so break; } // Some drives may hang if accessed again immediately following a standby or // idle command. We can fix this by delaying a bit before the next command. DelayMsec(1); // delay 1 msec. return((err == noErr) ? true : false); #endif } //-------------------------------------------------------------------------------- // Function: SpinDownDrive // // Description: Spins down the drive for the purpose of conserving power. No // action occurs if the drive is already spun down. This function // can also be used to flush the drive's write cache. // // Input: drive: Pointer to the drive record // // Output: Updates the curPowerMode variable of the drive record //-------------------------------------------------------------------------------- void SpinDownDrive(DriveRecPtr drive) { // If the drive is not spun down then issue an ATA Standby command (kMediaModeSuspend) to // spin it down. We use the Standby command instead of the Sleep command since // some drives (Quantum Fireball) take too long to spindown. if (!drive->isSpunDown) { SetPowerMode(drive, kMediaModeSuspend, 0); // put drive in standby mode } } //------------------------------------------------------------------------------ // FUNCTION: CheckUnexpectedRemoval // PURPOSE: Checks for offlined drives and offlines its volumes. // NOTE: Assumes it is called during non-interrupt time. // //------------------------------------------------------------------------------ void CheckUnexpectedRemoval(DriveRecPtr drive) { VolumeRecPtr vol; if (drive) // if drive is present… { if (drive->busState == kOFFLINE) // drive offline? { // Put all of the drive's volumes offline… vol = drive->nextVol; // first volume of drive while (vol) // while more volumes… { if (vol->driveStatus.diskInPlace > 0) // if volume online… { //vol->driveStatus.diskInPlace = 0; // offline volume; vol->driveStatus.diskInPlace = -1; // offline volume; //vol->driveStatus.installed = 0; // no drive installed vol->driveStatus.installed = -1; // no drive installed // If the driver was reentrant, we could do a PBOffline to offline // the volume and leave the ghost image, but since we are not // reentrant, we use the little known -2 disk insert // event for manual ejection which will either put up a "Please // insert the disk…" message or beep if not supported. //PostEvent(diskEvt, ((-2 << 16) | vol->vRefNum)); PostEvent(diskEvt, (vol->vRefNum)); } vol = (VolumeRecPtr)vol->nextVol; // next volume pointer } } } } //------------------------------------------------------------------------------ // Function: MountUnmountVolumes // Description: Mounts and unmounts all volume for the specified drive. // // Input: DriveRecPtr drive: drive with volumes to mount/unmount // // Output: Returns any errors that occur from PostEvent //------------------------------------------------------------------------------- OSErr MountUnmountVolumes( DriveRecPtr drive ) { VolumeRecPtr vol; OSErr mountErr = noErr; // First check for unexpected drive removal. Volumes will be deleted // if the drive is not present so we don't try to mount them below. CheckUnexpectedRemoval(drive); // Post a Disk Inserted event for all HFS volumes not yet mounted if (drive) // for each drive… { vol = drive->nextVol; // first volume structure while (vol) // for all volumes on drive… { if ((vol->driveStatus.diskInPlace != 0) && // if media in place, (vol->mountthispart) && // and volume to be mounted, (!vol->partmounted)) // and hasn't been done yet { mountErr = PostEvent(diskEvt, vol->vRefNum); if ( mountErr == noErr ) { vol->partmounted = true; } } vol = (VolumeRecPtr)vol->nextVol; // next per volume pointer } } return mountErr; // return error if one occurred }